import ftplib
import logging
import threading
import time
from contextlib import contextmanager

sync_logger = logging.getLogger('sync')

class FTPManager:
    _instance = None
    _lock = threading.Lock()
    _local = threading.local()

    def __new__(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super(FTPManager, cls).__new__(cls)
        return cls._instance

    def __init__(self):
        self.timeout = 300  # 5分钟超时
        self.retry_count = 3
        self.retry_delay = 5  # 重试间隔5秒
        self._connections = {}  # 存储FTP连接的字典
        self.sync_logger = logging.getLogger('sync')

    def _validate_connection(self, ftp):
        """验证FTP连接是否有效"""
        try:
            ftp.voidcmd('NOOP')
            return True
        except:
            return False

    def get_connection(self, host, user, password, initial_dir=None):
        """获取FTP连接，如果不存在则创建新连接"""
        connection_key = f"{host}:{user}"
        
        # 检查是否已有连接并且连接有效
        if connection_key in self._connections:
            try:
                self._connections[connection_key].voidcmd('NOOP')
                return self._connections[connection_key]
            except:
                self.sync_logger.info(f"FTP连接已断开，重新连接: {host}")
                try:
                    self._connections[connection_key].close()
                except:
                    pass
                del self._connections[connection_key]
        
        # 创建新连接
        retries = 0
        max_retries = 3
        last_error = None
        
        while retries < max_retries:
            try:
                self.sync_logger.info(f"正在连接FTP服务器: {host} (尝试 {retries+1}/{max_retries})")
                ftp = ftplib.FTP()
                ftp.set_pasv(True)  # 使用被动模式
                
                # 设置超时
                ftp.connect(host, 21, timeout=30)
                ftp.login(user, password)

                # 确保使用二进制模式
                ftp.voidcmd('TYPE I')
                
                # 测试不同的编码
                encodings = ['gbk', 'gb2312', 'utf-8', 'latin1']
                working_encoding = None
                
                for encoding in encodings:
                    try:
                        ftp.encoding = encoding
                        # 尝试列出根目录
                        test_list = []
                        ftp.dir(lambda x: test_list.append(x))
                        working_encoding = encoding
                        self.sync_logger.info(f"编码测试成功: {encoding}")
                        break
                    except Exception as e:
                        self.sync_logger.debug(f"编码 {encoding} 测试失败: {str(e)}")
                        continue
                
                if not working_encoding:
                    self.sync_logger.warning("所有编码测试失败，使用默认编码 GBK")
                    ftp.encoding = 'gbk'
                else:
                    ftp.encoding = working_encoding
                
                # 如果有初始目录，切换到该目录
                if initial_dir:
                    try:
                        # 检测目录中是否包含中文
                        if any('\u4e00' <= c <= '\u9fff' for c in initial_dir):
                            success = False
                            for enc in ['gbk', 'gb2312', 'utf-8']:
                                try:
                                    encoded_path = initial_dir.encode(enc).decode('latin1')
                                    ftp.cwd(encoded_path)
                                    self.sync_logger.info(f"成功使用 {enc} 编码访问目录: {initial_dir}")
                                    success = True
                                    break
                                except Exception as e:
                                    self.sync_logger.debug(f"{enc} 编码访问目录失败: {str(e)}")
                                    continue
                            
                            if not success:
                                self.sync_logger.warning(f"所有编码尝试失败，尝试直接访问目录: {initial_dir}")
                                ftp.cwd(initial_dir)
                        else:
                            ftp.cwd(initial_dir)
                    except ftplib.error_perm as e:
                        if "550" in str(e):
                            self.sync_logger.warning(f"FTP初始目录不存在: {initial_dir}")
                        else:
                            raise
                
                self.sync_logger.info(f"FTP连接成功: {host} (使用编码: {ftp.encoding})")
                self._connections[connection_key] = ftp
                return ftp
            
            except Exception as e:
                last_error = e
                retries += 1
                self.sync_logger.error(f"FTP连接失败 (尝试 {retries}/{max_retries}): {str(e)}")
                time.sleep(2)  # 等待2秒后重试
        
        # 所有重试都失败
        self.sync_logger.error(f"无法连接到FTP服务器 {host} 达到最大重试次数: {str(last_error)}")
        raise last_error

    def verify_connection(self, host, user, password, path=None):
        """验证FTP连接是否可用"""
        try:
            ftp = ftplib.FTP(host)
            ftp.timeout = self.timeout
            ftp.login(user, password)
            
            if path:
                try:
                    ftp.cwd(path)
                except ftplib.error_perm:
                    # 目录不存在是可以接受的，因为后面会创建
                    pass
            
            ftp.quit()
            return True
        except Exception as e:
            sync_logger.error(f"FTP连接验证失败: {str(e)}")
            return False

    def _create_ftp_directory(self, ftp, path):
        """创建FTP目录（支持多级目录）"""
        if not path:
            return

        current = ''
        for part in path.split('/'):
            if not part:
                continue
            current += '/' + part
            try:
                ftp.cwd(current)
            except ftplib.error_perm:
                try:
                    sync_logger.info(f"创建FTP目录: {current}")
                    ftp.mkd(current)
                    ftp.cwd(current)
                except ftplib.error_perm as e:
                    sync_logger.error(f"创建FTP目录失败: {current}, 错误: {e}")
                    raise

    def close_connection(self):
        """关闭当前线程的FTP连接"""
        if hasattr(self._local, 'ftp'):
            try:
                self._local.ftp.quit()
            except:
                pass
            finally:
                del self._local.ftp

    @contextmanager
    def connection(self, host, user, password, path=None):
        """FTP连接上下文管理器"""
        try:
            ftp = self.get_connection(host, user, password, path)
            yield ftp
        finally:
            self.close_connection()

    def test_server_settings(self, host, user, password):
        """测试FTP服务器的编码和模式设置"""
        try:
            self.sync_logger.info(f"开始测试FTP服务器设置: {host}")
            ftp = None
            
            try:
                # 建立新连接
                ftp = ftplib.FTP()
                ftp.set_pasv(True)
                ftp.connect(host, 21, timeout=30)
                ftp.login(user, password)
                self.sync_logger.info(f"FTP连接成功: {host}")
                
                # 测试编码
                encodings = ['gbk', 'utf-8', 'gb2312', 'latin1']
                working_encodings = []
                
                for encoding in encodings:
                    try:
                        ftp.encoding = encoding
                        # 尝试列出根目录
                        items = []
                        ftp.dir(lambda x: items.append(x))
                        self.sync_logger.info(f"编码 {encoding} 成功，获取到 {len(items)} 个项目")
                        working_encodings.append(encoding)
                    except Exception as e:
                        self.sync_logger.warning(f"编码 {encoding} 测试失败: {str(e)}")
                
                # 测试模式设置
                try:
                    # 测试二进制模式
                    ftp.voidcmd('TYPE I')
                    self.sync_logger.info("二进制模式设置成功")
                    
                    # 测试ASCII模式
                    ftp.voidcmd('TYPE A')
                    self.sync_logger.info("ASCII模式设置成功")
                except Exception as e:
                    self.sync_logger.error(f"模式设置测试失败: {str(e)}")
                
                # 返回结果
                return {
                    "connection_successful": True,
                    "working_encodings": working_encodings,
                    "recommended_encoding": working_encodings[0] if working_encodings else None,
                    "mode_switching_works": True
                }
                
            except Exception as e:
                self.sync_logger.error(f"FTP服务器设置测试失败: {str(e)}")
                return {
                    "connection_successful": False,
                    "error": str(e)
                }
            finally:
                if ftp:
                    try:
                        ftp.quit()
                    except:
                        pass
                        
        except Exception as e:
            self.sync_logger.error(f"FTP服务器设置测试失败: {str(e)}")
            return {
                "connection_successful": False,
                "error": str(e)
            }

# 创建全局实例
ftp_manager = FTPManager() 